package org.timeticker.impl;

import org.timeticker.Persistence;
import org.timeticker.ObjectList;
import org.apache.log4j.Logger;

import java.io.*;
import java.beans.XMLEncoder;
import java.beans.XMLDecoder;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.text.SimpleDateFormat;

/**
 * File System persistence implementation.
 * Implementation serialize activities using XMLDEncoder and de 
 *
 * @author Ivan Latysh
 * @version 0.1
 * @since 16-Jul-2007 : 8:10:24 PM
 */
public class FSPersistenceImpl implements Persistence {
  // Error prefix FSP00003

  /** Logger */
  protected Logger logger = Logger.getLogger(this.getClass().getName());
  /** Filename date formatter */
  protected static final SimpleDateFormat filenameFormat = new SimpleDateFormat("yyyyMMdd");
  /** File system root */
  protected File root;

  /**
   * Construct a new File System persistence manager
   *
   * @param root filesystem root
   */
  public FSPersistenceImpl(File root) {
    this.root = root;
  }

  public void persistObjectList(String key, ObjectList list) throws IllegalArgumentException, Exception {
    if (null== list || null == key) throw new IllegalArgumentException("Key and List must not be null");

    XMLEncoder encoder = null;
    OutputStream out = null;
    File file = null;
    try {
      file = new File(root, getFilenameFromKey(key));
      out = new FileOutputStream(file);
      // create a new XML Encoder
      encoder = new XMLEncoder(out);
      // serialize given listImpl
      encoder.writeObject(list);
      // flush
    } catch (Exception e) {
      logger.error("Unable to save object list {key="+key+"} {list="+list+"}.", e);
      throw e;
    } finally {
      if (null!=encoder) {
        // flush, close and close output stream
        encoder.close();
      }
      logger.debug("Saved {"+(null!=file ?file.getAbsolutePath() :"null")+"}.");
    }
  }

  public ObjectList restoreObjectList(String key) throws IllegalArgumentException, Exception {
    if (null == key) throw new IllegalArgumentException("Key must not be null");

    XMLDecoder decoder = null;
    File file = null;
    try {
      file = new File(root, getFilenameFromKey(key));
      // create new XML Decoder
      decoder = new XMLDecoder(new FileInputStream(file));
      // read object
      Object obj = decoder.readObject();
      if (!(obj instanceof ObjectList)) throw new ClassCastException("Expecting {"+ ObjectList.class.getName()+"} when got {"+obj.getClass().getName()+"}");
      // return loaded object
      return (ObjectList)obj;
    } catch (Exception ex) {
      logger.error("Unable to load ObjectList for key {"+key+"}.", ex);
      throw ex;
    } finally {
      if (null!=decoder) {
        // close input stream assochiated with this decoder
        decoder.close();
      }
      logger.debug("Loaded {"+(null!=file ?file.getAbsolutePath() :"null")+"}.");
    }
  }

  /**
   * Check if file for given key exists
   *
   * @param key key
   * @return <tt>true</tt> when file exists, <tt>false</tt> otherwise
   */
  public boolean isObjectListExists(String key) {     
    try {
      File listFile = new File(root, getFilenameFromKey(key));
      if (logger.isDebugEnabled() && listFile.isFile()) logger.debug("Found {"+listFile.getAbsolutePath()+"}");
      return listFile.isFile();
    } catch (Exception ex) {
      logger.error("[FSP00002] Unable to check if object list exist for key {"+key+"}", ex);
    }
    return false;
  }

  /**
   * Return list of available keys for given pattern
   *
   * @param pattern regex pattern
   * @return list of matching keys
   */
  public List<String> getKeyList(String pattern) {
    // prepare empty list list
    List<String> keys = new ArrayList<String>();
    // check if pattern is given
    if (null==pattern) return keys;
    // compile pattern
    final Pattern p = Pattern.compile(pattern);
    // list all files that matched given pattern
    File[] files = root.listFiles(new FilenameFilter(){
      public boolean accept(File dir, String name) {
        String key = getKeyFromFilename(name);
        Matcher m = p.matcher(key);
        return m.matches();
      }
    });
    // prepare key list
    for (File f :files) keys.add(getKeyFromFilename(f.getName()));
    // return keys
    return keys;
  }

  /**
   * Return a file name from given key
   *
   * @param key key
   * @return filename
   */
  protected static String getFilenameFromKey(String key) {
    return key+".xml";
  }

  /**
   * Return key from given filename
   *
   * @param filename file name
   * @return key
   */
  protected static String getKeyFromFilename(String filename) {
    if (null==filename) return filename;
    int extindex = filename.lastIndexOf(".xml");
    return filename.substring(0, (extindex>0 ?extindex :filename.length()));
  }

}
